<?php

namespace qrcode;

define('QRSPEC_VERSION_MAX', 40);
define('QRSPEC_WIDTH_MAX', 177);

class QRcode
{

    public $version;
    public $width;
    public $data;

    private $bestMask = true;
    private $defaultMask = 2;

    //----------------------------------------------------------------------
    public function encodeMask(QRinput $input, $mask)
    {
        if ($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) {
            throw new \Exception('wrong version');
        }
        if ($input->getErrorCorrectionLevel() > 3) {
            throw new \Exception('wrong level');
        }

        $raw = new QRrawcode($input);

        QRtools::markTime('after_raw');

        $version = $raw->version;
        $width   = QRspec::getWidth($version);
        $frame   = QRspec::newFrame($version);

        $filler = new FrameFiller($width, $frame);
        if (is_null($filler)) {
            return NULL;
        }

        // inteleaved data and ecc codes
        for ($i = 0; $i < $raw->dataLength + $raw->eccLength; $i++) {
            $code = $raw->getCode();
            $bit  = 0x80;
            for ($j = 0; $j < 8; $j++) {
                $addr = $filler->next();
                $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
                $bit = $bit >> 1;
            }
        }

        QRtools::markTime('after_filler');

        unset($raw);

        // remainder bits
        $j = QRspec::getRemainder($version);
        for ($i = 0; $i < $j; $i++) {
            $addr = $filler->next();
            $filler->setFrameAt($addr, 0x02);
        }

        $frame = $filler->frame;
        unset($filler);

        // masking
        $maskObj = new QRmask();
        if ($mask < 0) {

            if ($this->bestMask) {
                $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel());
            } else {
                $masked = $maskObj->makeMask($width, $frame, (intval($this->defaultMask) % 8), $input->getErrorCorrectionLevel());
            }
        } else {
            $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel());
        }

        if ($masked == NULL) {
            return NULL;
        }

        QRtools::markTime('after_mask');

        $this->version = $version;
        $this->width   = $width;
        $this->data    = $masked;

        return $this;
    }

    //----------------------------------------------------------------------
    public function encodeInput(QRinput $input)
    {
        return $this->encodeMask($input, -1);
    }

    //----------------------------------------------------------------------
    public function encodeString8bit($string, $version, $level)
    {
        if ($string == NULL) {
            throw new \Exception('empty string!');
        }

        $input = new QRinput($version, $level);
        if ($input == NULL) return NULL;

        $ret = $input->append($input, 2, strlen($string));
        if ($ret < 0) {
            unset($input);
            return NULL;
        }
        return $this->encodeInput($input);
    }

    //----------------------------------------------------------------------
    public function encodeString($string, $version, $level, $hint, $casesensitive)
    {

        if ($hint != 2 && $hint != 3) {
            throw new \Exception('bad hint');
        }

        $input = new QRinput($version, $level);
        if ($input == NULL) return NULL;

        $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive);
        if ($ret < 0) {
            return NULL;
        }

        return $this->encodeInput($input);
    }

    /**
     * 生成二维码
     * @param $text //二维码包含的内容，可以是链接、文字、json字符串等等
     * @param bool $outfile //默认为false，不生成文件，只将二维码图片内容返回，用户自行输出；否则需要给出存放生成二维码图片的文件名及路径
     * @param null $logo //二维码logo
     * @param int $level //默认为0，这个参数可传递的值分别是L(0，7%)、M(1，15%)、Q(2，25%)、H(3，30%)，这个参数控制二维码容错率，不同的参数表示二维码可被覆盖的区域百分比，也就是被覆盖的区域还能识别
     * @param int $size //控制生成图片的大小，默认为4
     * @param int $margin //控制生成二维码的空白区域大小
     * @return string|resource
     * @throws \Exception
     */
    public static function png($text, $outfile = false, $logo=null, $level = 0, $size = 10, $margin = 1)
    {
        $enc = QRencode::factory($level, $size, $margin);
        return $enc->encodePNG($text, $outfile, $logo);
    }

    //----------------------------------------------------------------------
    public static function text($text, $outfile = false, $level = 0, $size = 3, $margin = 4)
    {
        $enc = QRencode::factory($level, $size, $margin);
        return $enc->encode($text, $outfile);
    }

    //----------------------------------------------------------------------
    public static function raw($text, $outfile = false, $level = 0, $size = 3, $margin = 4)
    {
        $enc = QRencode::factory($level, $size, $margin);
        return $enc->encodeRAW($text);
    }
}
